package com.zyg.seckill.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zyg.common.utils.MyDate;
import com.zyg.seckill.dao.TbSeckillGoodsMapper;
import com.zyg.seckill.entity.TbSeckillGoods;
import com.zyg.seckill.service.SeckillService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
@EnableScheduling
public class SeckillServiceImpl implements SeckillService {

    @Autowired
    private TbSeckillGoodsMapper seckillGoodsMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;

    //1. 定时加载秒杀商品列表到redis中(每隔30秒加载一次)
    @Scheduled(cron = "0/30 * * * * ?")
    public void loadDataToRedis(){
        // System.out.println("当前时间：" + new Date().toLocaleString());
        //1.1 得到所有的时间段
        List<Date> dateMenus = MyDate.getDateMenus();
        //1.2 遍历五个时间段
        for (Date dateMenu : dateMenus) {
            //1.3 定义查询条件
            QueryWrapper<TbSeckillGoods> wrapper = new QueryWrapper<>();
            wrapper.eq("status","1");           //代表审核通过
            wrapper.ge("start_time",dateMenu);      //开始时间大于当前遍历时间段
            wrapper.le("end_time",MyDate.addDateHour(dateMenu,2)); //结束时间大于当前时间加2小时
            wrapper.gt("stock_count",0);        //库存量大于0

            //从redis得到某个时间段的商品id列表
            Set<Object> ids = redisTemplate.boundHashOps("seckillGoods_" + MyDate.getDateStr(dateMenu)).keys();
            //要查询的秒杀商品不在指定的ids列表中
            if(ids != null && ids.size() > 0) {
                wrapper.notIn("id", ids);
            }
            // 开始进行查询
            List<TbSeckillGoods> seckillGoods = seckillGoodsMapper.selectList(wrapper);
            //得到商品后将商品放到redis中
            BoundHashOperations<String, Object, Object> boundHashOperations = redisTemplate.boundHashOps("seckillGoods_" + MyDate.getDateStr(dateMenu));
            for (TbSeckillGoods seckillGood : seckillGoods) {

                //保存商品
                boundHashOperations.put(seckillGood.getId() + "", JSON.toJSONString(seckillGood));
                //设置过期时间
                boundHashOperations.expire((MyDate.addDateHour(dateMenu,2).getTime() - new Date().getTime())/1000, TimeUnit.SECONDS);
                //为了避免重复超卖，可以指定一个key(SeckillGoodsCountList_商品id），存放一个list队列（这个队列数据一个数组：【长度为商品库存量，内容可以为商品id（不作限制）】）
                Integer len = seckillGood.getStockCount();
                String[] secIds = new String[len];
                for (int i = 0; i < len; i++) {
                    secIds[i] = seckillGood.getId() + "";
                }
                //seckillGood.getId(): 代表的秒杀商品的id
                redisTemplate.boundListOps("SeckillGoodsCountList_" + seckillGood.getId()).leftPushAll(secIds);

                //1.11 为了保证扣减库存的精准性，我们可以使用一个Hash值，放到redis中，（大key可以任意，小key为商品id，值为：商品库存量）
                redisTemplate.boundHashOps("SeckillGoodsCount").put(seckillGood.getId()+"" ,seckillGood.getStockCount()+"");
            }


        }

    }

    //根据时间段获取商品列表
    @Override
    public List<TbSeckillGoods> getSeckillGoods() {
        //得到当前时间段
        String dateStr = MyDate.getDateStr(MyDate.getDateMenus().get(0));
        //从redis中得到商品
        BoundHashOperations<String, String, String> operations = redisTemplate.boundHashOps("seckillGoods_" + dateStr);
        //得到所有的值的集合
        List<String> values = operations.values();
        //定义返回的秒杀商品列表集合
        List<TbSeckillGoods> goodsList = new ArrayList<>();
        for (String value : values) {
            goodsList.add(JSON.parseObject(value,TbSeckillGoods.class));
        }
        //2.5 返回秒杀商品列表
        return goodsList;
    }

    //功能: 3. 根据商品id查询商品

    @Override
    public TbSeckillGoods findSeckillGoods(Integer id) {
        //3查找到当前时间段的商品列表信息
        List<TbSeckillGoods> seckillGoods = getSeckillGoods();
        //遍历集合，根据id找到秒杀商品对象
        // 使用stream写法
        // List<TbSeckillGoods> collect = seckillGoods.stream().filter(f -> f.getId().intValue() == id.intValue()).collect(Collectors.toList());
        // return collect.get(0);

        //直接使用for写
        for (TbSeckillGoods seckillGood : seckillGoods) {
            if(seckillGood.getId().intValue() == id.intValue()){
                return seckillGood;
            }
        }
        return null;
    }
}